package com.uyii.springbootgeodesy;

import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GlobalCoordinates;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class GeospatialService {

    private final GeodeticCalculator geoCalc = new GeodeticCalculator();
    private final Ellipsoid reference = Ellipsoid.WGS84;

    public double calculateDistance(double latitude1, double longitude1, double latitude2, double longitude2) {
        GlobalCoordinates source = new GlobalCoordinates(latitude1, longitude1);
        GlobalCoordinates destination = new GlobalCoordinates(latitude2, longitude2);
        return geoCalc.calculateGeodeticCurve(reference, source, destination).getEllipsoidalDistance();
    }

    public double calculateAzimuth(double latitude1, double longitude1, double latitude2, double longitude2) {
        GlobalCoordinates source = new GlobalCoordinates(latitude1, longitude1);
        GlobalCoordinates destination = new GlobalCoordinates(latitude2, longitude2);
        return geoCalc.calculateGeodeticCurve(reference, source, destination).getAzimuth();
    }

    public GlobalCoordinates calculateMidpoint(double latitude1, double longitude1, double latitude2, double longitude2) {
        // 简单实现，两点的平均值作为中点
        double midLatitude = (latitude1 + latitude2) / 2;
        double midLongitude = (longitude1 + longitude2) / 2;
        return new GlobalCoordinates(midLatitude, midLongitude);
    }

    public double calculateETA(double latitude1, double longitude1, double latitude2, double longitude2, double speed) {
        double distance = calculateDistance(latitude1, longitude1, latitude2, longitude2);
        return distance / speed; // 返回时间，单位为小时
    }

    public boolean isPointInPolygon(double latitude, double longitude, List<GlobalCoordinates> polygon) {
        int intersectCount = 0;
        for (int i = 0; i < polygon.size() - 1; i++) {
            GlobalCoordinates p1 = polygon.get(i);
            GlobalCoordinates p2 = polygon.get(i + 1);
            if (rayCastIntersect(latitude, longitude, p1, p2)) {
                intersectCount++;
            }
        }
        return (intersectCount % 2) == 1; // 奇数个交点表示点在多边形内
    }

    private boolean rayCastIntersect(double latitude, double longitude, GlobalCoordinates p1, GlobalCoordinates p2) {
        if (p1.getLongitude() > p2.getLongitude()) {
            GlobalCoordinates temp = p1;
            p1 = p2;
            p2 = temp;
        }
        if (longitude == p1.getLongitude() || longitude == p2.getLongitude()) {
            longitude += 0.0001; // 避免在顶点上的情况
        }
        if (longitude > p2.getLongitude() || longitude < p1.getLongitude()) {
            return false;
        }
        if (latitude > Math.max(p1.getLatitude(), p2.getLatitude())) {
            return false;
        }
        if (latitude < Math.min(p1.getLatitude(), p2.getLatitude())) {
            return true;
        }
        double red = (longitude - p1.getLongitude()) / (p2.getLongitude() - p1.getLongitude());
        double blue = (latitude - p1.getLatitude()) / (p2.getLatitude() - p1.getLatitude());
        return red >= blue;
    }

    public GlobalCoordinates translatePoint(double latitude, double longitude, double distanceInMeters, double bearing) {
        GlobalCoordinates startPoint = new GlobalCoordinates(latitude, longitude);
        double angularDistance = distanceInMeters / Ellipsoid.WGS84.getSemiMajorAxis();

        double newLat = Math.asin(Math.sin(Math.toRadians(latitude)) * Math.cos(angularDistance) +
                Math.cos(Math.toRadians(latitude)) * Math.sin(angularDistance) * Math.cos(Math.toRadians(bearing)));
        double newLon = Math.toRadians(longitude) +
                Math.atan2(Math.sin(Math.toRadians(bearing)) * Math.sin(angularDistance) * Math.cos(Math.toRadians(latitude)),
                        Math.cos(angularDistance) - Math.sin(Math.toRadians(latitude)) * Math.sin(newLat));

        return new GlobalCoordinates(Math.toDegrees(newLat), Math.toDegrees(newLon));
    }

    public double calculatePolygonArea(List<GlobalCoordinates> polygon) {
        double area = 0.0;
        for (int i = 0; i < polygon.size() - 1; i++) {
            GlobalCoordinates p1 = polygon.get(i);
            GlobalCoordinates p2 = polygon.get(i + 1);
            area += Math.toRadians(p2.getLongitude() - p1.getLongitude()) *
                    (2 + Math.sin(Math.toRadians(p1.getLatitude())) + Math.sin(Math.toRadians(p2.getLatitude())));
        }
        area = area * Ellipsoid.WGS84.getSemiMajorAxis() * Ellipsoid.WGS84.getSemiMajorAxis() / 2.0;
        return Math.abs(area); // 返回面积，单位为平方米
    }

    public BoundingBox calculateBoundingBox(double latitude, double longitude, double distanceInMeters) {
        GlobalCoordinates center = new GlobalCoordinates(latitude, longitude);
        double earthRadius = 6371000; // 地球半径，单位为米

        double latOffset = distanceInMeters / earthRadius * (180 / Math.PI);
        double lonOffset = distanceInMeters / (earthRadius * Math.cos(Math.toRadians(latitude))) * (180 / Math.PI);

        double minLatitude = latitude - latOffset;
        double maxLatitude = latitude + latOffset;
        double minLongitude = longitude - lonOffset;
        double maxLongitude = longitude + lonOffset;

        return new BoundingBox(minLatitude, maxLatitude, minLongitude, maxLongitude);
    }

}